package com.kangfuqiang.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <pre> 
 * Title:不重复的随机整数类
 * Description: 用于依次获取 [MIN,MAX] 范围内的不重复随机整数；
 * 使用说明：需要new一个该类的实例（传入min和max参数），然后 使用该实例 依次获得不重复的随机整数；
 * 
 * ps：1M 的整数范围时，该实例占用的相关内存约为 4M 。
 * 
 * </pre>
 */
public class NoRepeatRandomInt {
    private static Logger log = LoggerFactory.getLogger(NoRepeatRandomInt.class);
    
    /** 最小值（包含） */
    public final int MIN;
    /** 最大值（包含） */
    public final int MAX;
    
    
    /** 计数器，记录已经领取不重复的随机整数的次数 */
    private int counter = 0;
    
    /** 保存 [MIN,MAX] 范围内的所有整数*/
    private int[] numArr ;   
    
    /**
     * @param min
     * @param max
     * @exception IllegalArgumentException 如果 min > max，则抛出该异常
     */
    public NoRepeatRandomInt(int min, int max) {
        if(min>max){
            String str = String.format("构造器参数错误，最小值不能大于最大值！min:%s,max:%s", min, max);
            log.error(str);
            throw new IllegalArgumentException( str );
        }
        this.MIN = min;
        this.MAX = max;
        numArr = new int[this.getOriginalCnt()];
        for(int i=0; i<numArr.length; i++){
            numArr[i] = i + this.MIN;
        }
    }
    
    /**
     * 领取 下一个不重复的随机整数
     * @return
     * @exception RuntimeException 如果 领取次数 超过 总个数范围，将抛出运行时异常
     */
    public int next(){
        if(this.getRemainCnt()<=0){
            String str = String.format("领取次数 超过 总个数范围（%d个）!", this.getOriginalCnt());
            log.error(str);
            throw new RuntimeException(str);
        }
        int idx = (int)(Math.random() * this.getRemainCnt());
        int ret = numArr[idx];
        numArr[idx] = numArr[this.getRemainCnt()-1];
        numArr[this.getRemainCnt()-1] = ret; 
        this.counter++;
        return ret;
    }
    
    /** 获得原始个数 */
    private int getOriginalCnt(){
        return this.MAX - this.MIN + 1;
    }
    
    /** 获得剩余个数 */
    private int getRemainCnt(){
        return this.MAX - this.MIN + 1 - counter;
    }
}
